package com.xuecheng.content.service.impl;

import com.xuecheng.content.mapper.WorkMapper;
import com.xuecheng.content.mapper.WorkRecordMapper;
import com.xuecheng.content.model.dto.RecommendWorkDto;
import com.xuecheng.content.model.po.Work;
import com.xuecheng.content.model.po.WorkRecord;
import com.xuecheng.content.service.RecommendService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class RecommendServiceImpl implements RecommendService {

    @Autowired
    private WorkMapper workMapper;

    @Autowired
    private WorkRecordMapper workRecordMapper;

    @Override
    public List<RecommendWorkDto> recommendWorks(Long userId) {
        // 1. 获取所有作业记录
        List<WorkRecord> allRecords = workRecordMapper.selectList(null);
        
        // 2. 构建用户-作业评分矩阵
        Map<Long, Map<Long, Double>> userWorkMatrix = new HashMap<>();
        for (WorkRecord record : allRecords) {
            Long recordUserId = record.getUserId();
            Long workId = record.getWorkId();
            Double score = record.getScore() != null ? record.getScore().doubleValue() : 0.0;
            
            userWorkMatrix.computeIfAbsent(recordUserId, k -> new HashMap<>())
                    .put(workId, score);
        }

        // 3. 计算用户相似度
        Map<Long, Double> userSimilarities = calculateUserSimilarities(userId, userWorkMatrix);

        // 4. 获取当前用户未完成的作业
        List<Work> allWorks = workMapper.selectList(null);
        Set<Long> completedWorks = userWorkMatrix.getOrDefault(userId, new HashMap<>()).keySet();
        List<Work> uncompletedWorks = allWorks.stream()
                .filter(work -> !completedWorks.contains(work.getWorkId()))
                .collect(Collectors.toList());

        // 5. 计算推荐分数
        List<RecommendWorkDto> recommendations = new ArrayList<>();
        for (Work work : uncompletedWorks) {
            double score = calculateRecommendationScore(work.getWorkId(), userSimilarities, userWorkMatrix);
            recommendations.add(new RecommendWorkDto(work, score));
        }

        // 6. 按推荐分数排序并返回前10个
        return recommendations.stream()
                .sorted(Comparator.comparing(RecommendWorkDto::getRecommendationScore).reversed())
                .limit(10)
                .collect(Collectors.toList());
    }

    private Map<Long, Double> calculateUserSimilarities(Long targetUserId, Map<Long, Map<Long, Double>> userWorkMatrix) {
        Map<Long, Double> similarities = new HashMap<>();
        Map<Long, Double> targetUserRatings = userWorkMatrix.getOrDefault(targetUserId, new HashMap<>());

        for (Long otherUserId : userWorkMatrix.keySet()) {
            if (otherUserId.equals(targetUserId)) continue;

            Map<Long, Double> otherUserRatings = userWorkMatrix.get(otherUserId);
            double similarity = calculateCosineSimilarity(targetUserRatings, otherUserRatings);
            similarities.put(otherUserId, similarity);
        }

        return similarities;
    }

    private double calculateCosineSimilarity(Map<Long, Double> ratings1, Map<Long, Double> ratings2) {
        double dotProduct = 0.0;
        double norm1 = 0.0;
        double norm2 = 0.0;

        Set<Long> commonWorks = new HashSet<>(ratings1.keySet());
        commonWorks.retainAll(ratings2.keySet());

        if (commonWorks.isEmpty()) return 0.0;

        for (Long workId : commonWorks) {
            double rating1 = ratings1.get(workId);
            double rating2 = ratings2.get(workId);
            dotProduct += rating1 * rating2;
            norm1 += rating1 * rating1;
            norm2 += rating2 * rating2;
        }

        if (norm1 == 0.0 || norm2 == 0.0) return 0.0;

        return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
    }

    private double calculateRecommendationScore(Long workId, Map<Long, Double> userSimilarities, 
                                             Map<Long, Map<Long, Double>> userWorkMatrix) {
        double weightedSum = 0.0;
        double similaritySum = 0.0;

        for (Map.Entry<Long, Double> entry : userSimilarities.entrySet()) {
            Long otherUserId = entry.getKey();
            double similarity = entry.getValue();
            
            Map<Long, Double> otherUserRatings = userWorkMatrix.get(otherUserId);
            if (otherUserRatings != null && otherUserRatings.containsKey(workId)) {
                double rating = otherUserRatings.get(workId);
                weightedSum += similarity * rating;
                similaritySum += Math.abs(similarity);
            }
        }

        return similaritySum > 0 ? weightedSum / similaritySum : 0.0;
    }
} 